#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

/* 生成 RGB565 像素值 */
inline static unsigned short int 
make_rgb565( unsigned char r, 
             unsigned char g, 
             unsigned char b )
{
    /* r 1.右移  3 位是为了保留字节高 5 位有效，
         2.位与 31 是为了将除低 5 位以外的位值变为零，
         3.左移 11 位是为了将 r 的移到两字节返回值的高 15 ~ 11 位上去 */
    /* g 1.右移  2 位是为了保留字节高 6 位有效，
         2.位与 63 是为了将除低 6 位以外的位值变为零，
         3.左移  5 位是为了将 r 的移到两字节返回值的中 10 ~  5 位上去 */
    /* b 1.右移 3 位是为了保留字节高 5 位有效，
         2.位与 31 是为了将除低 5 位以外的位值变为零 */
    /* 最后将 r 、g 、b 三个值组成两字节返回值返回 */
    return ( ( ( ( r >> 3 ) & 31 ) << 11) | 
             ( ( ( g >> 2 ) & 63 ) << 5 ) |
               ( ( b >> 3 ) & 31 ) );
}

//<--将 < 替换为 * --
/* 这个结构描述了显示卡的特性
struct fb_var_screeninfo
{
    __u32 xres;                // visible resolution
    __u32 yres;
    __u32 xres_virtual;        // virtual resolution
    __u32 yres_virtual;
    __u32 xoffset;             // offset from virtual to visible resolution
    __u32 yoffset;
    __u32 bits_per_pixel;      // guess what
    __u32 grayscale;           // != 0 Gray levels instead of colors
    struct fb_bitfield red;    // bitfield in fb mem if true color,
    struct fb_bitfield green;  // else only length is significant
    struct fb_bitfield blue;
    struct fb_bitfield transp; // transparency
    __u32 nonstd;              // != 0 Non standard pixel format
    __u32 activate;            // see FB_ACTIVATE_* 
    __u32 height;              // height of picture in mm
    __u32 width;               // width of picture in mm
    __u32 accel_flags;         // acceleration flags (hints)
    
    // Timing: All values in pixclocks, except pixclock (of course)
    __u32 pixclock;     // pixel clock in ps (pico seconds)
    __u32 left_margin;  // time from sync to picture
    __u32 right_margin; // time from picture to sync
    __u32 upper_margin; // time from sync to picture
    __u32 lower_margin;
    __u32 hsync_len;    // length of horizontal sync
    __u32 vsync_len;    // length of vertical sync 
    __u32 sync;         // see FB_SYNC_* 
    __u32 vmode;        // see FB_VMODE_* 
    __u32 reserved[6];  // Reserved for future compatibility
};

// 这个结构在显卡被设定模式后创建，它描述显示卡的属性，并且系统运行时不能被修改；
// 比如 FrameBuffer 内存的起始地址。
// 它依赖于被设定的模式，当一个模式被设定后，内存信息由显示卡硬件给出，内存的位置等信息就不可以修改。
struct fb_fix_screeninfo 
{
    char id[16];              // identification string eg "TT Builtin"
    unsigned long smem_start; // Start of frame buffer mem
    
    // physical address
    __u32 smem_len;           // Length of frame buffer mem
    __u32 type;               // see FB_TYPE_* 
    __u32 type_aux;           // Interleave for interleaved Planes
    __u32 visual;             // see FB_VISUAL_*
    __u16 xpanstep;           // zero if no hardware panning
    __u16 ypanstep;           // zero if no hardware panning
    __u16 ywrapstep;          // zero if no hardware ywrap
    __u32 line_length;        // length of a line in bytes
    unsigned long mmio_start; // Start of Memory Mapped I/O
    
    // physical address
    __u32 mmio_len;    // Length of Memory Mapped I/O
    __u32 accel;       // Type of acceleration available
    __u16 reserved[3]; // Reserved for future compatibility
};


// 描述设备无关的颜色映射信息。
// 可以通过 FBIOGETCMAP 和 FBIOPUTCMAP 对应的 ioctl 操作设定或获取颜色映射信息。
struct fb_cmap 
{
    __u32 start;   // First entry
    __u32 len;     // Number of entries
    __u16 *red;    // Red values
    __u16 *green;
    __u16 *blue;
    __u16 *transp; // transparency, can be NULL
};


// 定义当显卡的当前状态；
// fb_info 结构仅在内核中可见，在这个结构中有一个 fb_ops 指针，指向驱动设备工作所需的函数集。
struct fb_info 
{
    char modename[40];            // default video mode
    kdev_t node;
    int flags;
    int open;                     // Has this been open already ?
    #define FBINFO_FLAG_MODULE 1  // Low-level driver is a module
    struct fb_var_screeninfo var; // Current var
    struct fb_fix_screeninfo fix; // Current fix
    struct fb_monspecs monspecs;  // Current Monitor specs
    struct fb_cmap cmap;          // Current cmap
    struct fb_ops *fbops;
    char *screen_base;            // Virtual address
    struct display *disp;         // initial display variable
    struct vc_data *display_fg;   // Console visible on this display
    char fontname[40];            // default font name
    devfs_handle_t devfs_handle;  // Devfs handle for new name
    devfs_handle_t devfs_lhandle; // Devfs handle for compat. symlink
    int (*changevar)(int);        // tell console var has changed
    int (*switch_con)(int, struct fb_info*);
    
    // tell fb to switch consoles
    int (*updatevar)(int, struct fb_info*);
    
    // tell fb to update the vars
    void (*blank)(int, struct fb_info*); // tell fb to (un)blank the screen
    
    // arg = 0: unblank
    // arg > 0: VESA level (arg-1)
    // Fake palette of 16 colors and the cursor's color for non palette mode
    void *pseudo_palette; 
    
    // From here on everything is device dependent
    void *par;
};


// 用户应用可以使用 ioctl() 系统调用来操作设备，这个结构就是用一支持 ioctl() 的这些操作的。
struct fb_ops 
{
    // open/release and usage marking
    struct module *owner;
    int (*fb_open)( struct fb_info *info, 
                           int      user );
    int (*fb_release)( struct fb_info *info, 
                              int      user );
    
    // get non settable parameters
    int (*fb_get_fix)( struct fb_fix_screeninfo *fix, 
                              int                con, 
                       struct fb_info           *info );
                       
    // get settable parameters
    int (*fb_get_var)( struct fb_var_screeninfo *var, 
                              int                con, 
                              struct fb_info    *info);
                              
    // set settable parameters
    int (*fb_set_var)( struct fb_var_screeninfo *var, 
                              int                con,
                       struct fb_info           *info );
                       
    // get colormap
    int (*fb_get_cmap)( struct fb_cmap *cmap, 
                               int      kspc, 
                               int      con, 
                        struct fb_info *info );
    
    // set colormap
    int (*fb_set_cmap)( struct fb_cmap *cmap, 
                               int      kspc, 
                               int      con, 
                        struct fb_info *info );
    
    // pan display (optional)
    int (*fb_pan_display)( struct fb_var_screeninfo *var, 
                                  int                con, 
                           struct fb_info           *info );
    
    // perform fb specific ioctl (optional)
    int (*fb_ioctl)( struct inode   *inode, 
                     struct file    *file, 
                     unsigned int    cmd, 
                     unsigned long   arg, 
                     int             con, 
                     struct fb_info *info );
    
    // perform fb specific mmap
    int (*fb_mmap)( struct fb_info        *info, 
                    struct file           *file, 
                    struct vm_area_struct *vma );
    
    // switch to/from raster image mode
    int (*fb_rasterimg)( struct fb_info *info, 
                                int      start );
};
*/

int 
main() 
{
    /* 本函数返回值 */
    int iResult = 0;
    /* FrameBuffer设备文件描述符 */
    int fdFrameBuffer = -1;
    /* 屏幕的可变信息结构变量 */
    struct fb_var_screeninfo sScreenInfoVar;
    /* 屏幕的固定信息结构变量 */
    struct fb_fix_screeninfo sScreenInfoFix;
    /* 显存字节大小 */
    unsigned long ulVideoMemByteSize = 0;
    /* 内存映射地址 */
    unsigned char* pucMapMem = 0;
    /* for 循环行计数器 */
    unsigned char ucForRow = 0;
    /* for 循环列计数器 */
    unsigned char ucForCol = 0;
    /* 屏幕行首地址指针 */
    unsigned char* pucRow = 0;
    /* W 字模 */
    unsigned short int uiWFont[6][6] = { 63488,     0,     0, 0,     0,     63488, 
                                         63488,     0, 63488, 63488, 0,     63488,
                                         63488,     0, 63488, 63488, 0,     63488,
                                         63488,     0, 63488, 63488, 0,     63488,
                                         63488, 63488,     0, 0,     63488, 63488,
                                             0, 63488,     0, 0,     63488, 0 };


    /* Z 字模 */
    unsigned short int uiZFont[6][6] = { 2016, 2016, 2016, 2016, 2016, 2016, 
                                         2016, 2016, 2016, 2016, 2016, 2016,
                                            0,    0,   0,  2016, 2016,    0,
                                            0,    0, 2016, 2016,    0,    0,
                                            0, 2016, 2016, 2016, 2016, 2016,
                                         2016, 2016, 2016, 2016, 2016, 2016 };


    /* H 字模 */
    unsigned short int uiHFont[6][6] = { 31, 31,  0, 0,  31, 31, 
                                         31, 31,  0, 0,  31, 31,
                                         31, 31, 31, 31, 31, 31,
                                         31, 31, 31, 31, 31, 31,
                                         31, 31,  0, 0,  31, 31,
                                         31, 31,  0, 0,  31, 31 };


    /* N 字模 */
    unsigned short int uiNFont[6][6] = { 63488,     0,     0, 0,     63488, 63488, 
                                         63488, 63488,     0, 0,     63488, 63488,
                                         63488, 63488, 63488, 0,     63488, 63488,
                                         63488, 63488, 63488, 63488, 63488, 63488,
                                         63488, 63488,     0, 63488, 63488, 63488,
                                         63488, 63488,     0, 0,     63488, 63488 };


    /* S 字模 */
    unsigned short int uiSFont[6][6] = {    0,    0, 2016, 2016, 2016, 2016, 
                                            0, 2016,    0,    0,    0, 2016,
                                         2016,    0,    0,    0,    0,    0,
                                            0, 2016, 2016, 2016,    0,    0,
                                         2016,    0,    0,    0, 2016,    0,
                                         2016, 2016, 2016, 2016, 2016, 2016 };


    /* C 字模 */
    unsigned short int uiCFont[6][6] = {  0,  0, 31,  0, 31, 31, 
                                          0, 31, 31,  0, 31, 31,
                                         31, 31,  0,  0,  0,  0,
                                         31, 31,  0,  0,  0,  0,
                                          0, 31, 31, 31, 31, 31,
                                          0,  0, 31, 31, 31, 31 };
    
    do /* 非循环，只是为了减少分支缩进 */
    {
        /* 1.可读写方式打开 framebuffer 设备文件 */
        fdFrameBuffer = open( "/dev/graphics/fb0", O_RDWR );
        if ( -1 == fdFrameBuffer )
        {
            printf( "Error: Don't Open FrameBuffer Device.\n" );
            iResult = -1;
            break;
        }


        /* ioctl 参数一：设备文件描述符;
                 参数二：命令编码，如：
                 　　　　FBIOGET_FSCREENINFO - 得到屏幕的固定信息(如：显示内存大小) 
                 　　　　FBIOGET_VSCREENINFO - 得到屏幕的可变信息(如：分辨率、象素结构、每扫描线的字节宽度)
                 　　　　FBIOPUT_VSCREENINFO - 设置屏幕的可变信息
                 　　　　FBIOGETCMAP         - 获取颜色映射信息
                 　　　　FBIOPUTCMAP         - 设置颜色映射信息
                 参数三：与命令编码对应的结构变量指针：
                 　　　　FBIOGET_FSCREENINFO 　                     对应 fb_fix_screeninfo 结构体； 
                 　　　　FBIOGET_VSCREENINFO 和 FBIOPUT_VSCREENINFO 对应 fb_var_screeninfo 结构体；
                 　　　　FBIOGETCMAP         和 FBIOPUTCMAP         对应 fb_cmap 结构体；
         */
        /* 2.得到屏幕的固定信息 */
        if ( -1 == ioctl( fdFrameBuffer, 
                          FBIOGET_FSCREENINFO, 
                          &sScreenInfoFix ) )
        {
            printf( "Error: Don't Read Out Fix Screen Information.\n" );
            iResult = -2;
            break;
        }


        /* 3.得到屏幕的可变信息 */
        if ( -1 == ioctl( fdFrameBuffer, 
                          FBIOGET_VSCREENINFO, 
                          &sScreenInfoVar ) ) 
        {
            printf( "Error: Don't Read Out Var Screen Information.\n" );
            iResult = -3;
            break;
        }
        
        /* 显存所需字节大小 = ( 屏幕宽 * 屏幕高 * 每像素位数 ) / 8 */
        printf( "xres = %d\nyres = %d\nbits_per_pixel = %d\n", 
                sScreenInfoVar.xres, 
                sScreenInfoVar.yres, 
                sScreenInfoVar.bits_per_pixel );
        printf( "xoffset:%d\nyoffset:%d\nline_length: %d\n", 
                sScreenInfoVar.xoffset, 
                sScreenInfoVar.yoffset, 
                sScreenInfoFix.line_length );
        /* Android 屏幕坐标系如下：
           --------- xres
           |
           |
           |
          yres
         */
ulVideoMemByteSize = ( sScreenInfoVar.xres * sScreenInfoVar.yres * sScreenInfoVar.bits_per_pixel ) / 8;
        
        /* 将 FrameBuffer 设备文件内容映射到内存中，对该内存区域的存取即是直接对该文件内容的读写 */
        pucMapMem = (unsigned char*)mmap( 0, /* 指向欲对应的内存起始地址，
                                                通常设为 0，代表让系统自动选定地址 */
                                          /* 将文件中多少字节对应到内存 */
                                          ulVideoMemByteSize, 
                                          /* 映射区域可被 读取 和 写入 */
                                          PROT_READ | PROT_WRITE, 
                                          /* 映射区域的写入数据会复制回文件内，
                                             而且允许其他映射该文件的进程共享 */
                                          MAP_SHARED, 
                                          /* FrameBuffer 文件描述符 */
                                          fdFrameBuffer, 
                                          /* 为文件映射的偏移量，
                                             通常设置为0，代表从文件最前方开始对应，
                                             必须是分页大小的整数倍 */
                                          0 );
        if ( -1 == (int)pucMapMem ) 
        {
            printf( "Error: Map FrameBuffer Device File to Memory Failed.\n" );
            iResult = -4;
            break;
        }
        
        /* 清屏 - 黑屏 */
        memset(pucMapMem, 0, ulVideoMemByteSize);
        
        for ( ucForRow = 0;
              ucForRow < 6;
              ucForRow++ )
        {
            /* 屏幕第n行首像素对应显存地址 = 行号(基于零) * 一行像素个数 * 每个像素字节个数 */
            pucRow = pucMapMem + 
                     ( ucForRow * 
                       sScreenInfoVar.xres * 
                       ( sScreenInfoVar.bits_per_pixel / 8 ) );
            /* W */ 
            memcpy( pucRow, uiWFont + ucForRow, 12 );
            
            /* Z */
            memcpy( pucRow + 15, uiZFont + ucForRow, 12 );
            
            /* H */
            memcpy( pucRow + 30, uiHFont + ucForRow, 12 );
            
            /* N */ 
            memcpy( pucRow + 45, uiNFont + ucForRow, 12 );
            
            /* S */
            memcpy( pucRow + 60, uiSFont + ucForRow, 12 );
            
            /* C */
            memcpy( pucRow + 75, uiCFont + ucForRow, 12 );            
        }
    
    }while( 0 );


    if ( 0 != pucMapMem )
    {
        /* 解除内存映射 */
        munmap( pucMapMem,            /* 映射内存起始地址 */
                ulVideoMemByteSize ); /* 欲取消的内存大小 */
        pucMapMem = 0;
    }
    
    if ( -1 != fdFrameBuffer )
    {
        /* 关闭文件 */
        close( fdFrameBuffer );
        fdFrameBuffer = -1;
    }
    
    return iResult;

}
